package simpleparser

import (
	"errors"
	"fmt"
	"strconv"
)

type Calculator struct {
	var_declare map[string]bool // 声明变量(Id)
	var_value   map[string]int  // 变量值记录
}

func NewCalculator() *Calculator {
	return &Calculator{
		make(map[string]bool),
		make(map[string]int),
	}
}

func (c *Calculator) Calc(inputs []string) {
	ret := &AstNode{Type: ProgramRoot}
	var err error

	for _, input := range inputs {
		// lexer
		tokenList, err := Tokenize(input)
		if err != nil {
			fmt.Printf("Lexer:%s -> Error:%s\n", input, err)
			continue
		}

		str := fmt.Sprintf("Lexer:\t%s -> \n", input)
		for {
			token := tokenList.Next()
			if token == nil {
				break
			}

			str += fmt.Sprintf("[%s %s]", TOKEN_TYPE_DESC[token.Type], token.Value)
		}

		fmt.Println(str)

		// parser
		tokenList.Reset()
		root, err := ParseItem(tokenList)
		if err != nil {
			fmt.Printf("Parser:%s -> Error:%s\n", input, err)
			continue
		}

		fmt.Printf("Parser:\t%s -> \n %v\n", input, root.ToString(0))

		ret.Children = append(ret.Children, root)

		fmt.Println("--------------------------------------------------")
	}

	if err != nil {
		fmt.Printf("Calc Parse error:%s\n", err)
	}

	// calc
	result, err := c.CalcItem(ret, "")
	if err != nil {
		fmt.Printf("CalcItem Error:%s\n", err)
	}

	fmt.Printf("Calc Result: %d\n", result)
	fmt.Println("--------------------------------------------------")
}

/**
 * 计算语法树结果
 * @param node 语法树节点
 * @param prefix 打印前缀（缩进）
 */
func (c *Calculator) CalcItem(node *AstNode, prefix string) (int, error) {
	if node == nil {
		return 0, errors.New("calc node is nil")
	}

	fmt.Printf("%s Calculating:%s\n", prefix, AST_NODE_DESC[node.Type])

	var ret int

	switch node.Type {
	case ProgramRoot:
		for _, child := range node.Children {
			v, err := c.CalcItem(child, "\t"+prefix)
			if err != nil {
				return 0, err
			}

			ret = v
		}

	case DeclareInt:
		if _, ok := c.var_declare[node.Value]; ok {
			return 0, errors.New(fmt.Sprintf("Repeat declared var %s", node.Value))
		} else {
			c.var_declare[node.Value] = true

			if len(node.Children) <= 0 {
				ret = 0
			} else {
				v1, err := c.CalcItem(node.Children[0], "\t"+prefix)
				if err != nil {
					return 0, err
				}
				c.var_value[node.Value] = v1

				ret = v1
			}
		}

	case AssignmentInt:
		if _, ok := c.var_declare[node.Value]; ok {
			v1, err := c.CalcItem(node.Children[0], "\t"+prefix)
			if err != nil {
				return 0, err
			}

			c.var_value[node.Value] = v1
			ret = v1
		} else {
			return 0, errors.New(fmt.Sprintf("Use undeclared var %s", node.Value))
		}

	case IdInt:
		if v, ok := c.var_value[node.Value]; ok {
			ret = v
		} else if _, ok := c.var_declare[node.Value]; ok {
			ret = 0
		} else {
			return 0, errors.New(fmt.Sprintf("Use undeclared var %s", node.Value))
		}

	case LiteralInt:
		ret, _ = strconv.Atoi(node.Value)

	case ExprAdd, ExprSub:
		v1, err := c.CalcItem(node.Children[0], "\t"+prefix)
		if err != nil {
			return 0, err
		}

		v2, err := c.CalcItem(node.Children[1], "\t"+prefix)
		if err != nil {
			return 0, err
		}

		if node.Value == "+" {
			ret = v1 + v2
		} else {
			ret = v1 - v2
		}

	case ExprMul, ExprDiv:
		v1, err := c.CalcItem(node.Children[0], "\t"+prefix)
		if err != nil {
			return 0, err
		}

		v2, err := c.CalcItem(node.Children[1], "\t"+prefix)
		if err != nil {
			return 0, err
		}

		if node.Value == "*" {
			ret = v1 * v2
		} else {
			ret = v1 / v2
		}

	default:
		return 0, errors.New("unknown calc ops - node type")
	}

	fmt.Printf("%s %s Result:%d\n", prefix, AST_NODE_DESC[node.Type], ret)
	return ret, nil
}
